from abc import ABCMeta, abstractmethod


"""
1、设计要点：
    过滤器模式中主要有三个角色，在设计过滤模式时要找到并区分这些角色。
    （1）过滤的目标：即要被过滤的对象，通常是一个对象数组（对象列表）。
    （2）过滤器：负责过滤不需要的对象，一般一个规则对应一个类。
    （3）过滤器链：即过滤器的集合，负责管理和维护过滤器，用这个对象进行过滤器，它包含的每一个子过滤器都会进行一次过滤。这个类并不总是必要的，
    但如果有多个过滤器，有这个类会带来极大的便利。
2、过滤器模式的优缺点
    优点：
    （1）将对象的过滤、校验逻辑抽离出来，降低系统的复杂度。
    （2）过滤规则可实现重复利用。

    缺点：
    性能较低，每个过滤器对每一个元素都会进行遍历。如果有n个元素，m个过滤器，则负责度为O(mn)

3、应用场景
    （1）敏感词过滤、舆情监测。
    （2）需要对对象列表（或数据列表）进行校验、审查或预处理的场景。
    （3）对网络接口的请求和响应进行拦截，例如对每一个请求和响应记录日志，一遍日后分析。

在Scrapy的爬虫和下载器中间件、Django的中间件中都有点像应用了过滤器的模式。
"""
class Filter(metaclass=ABCMeta):
    """过滤器"""

    @abstractmethod
    def doFilter(self, elements):
        pass


class FilterChain(Filter):
    """过滤器链"""
    def __init__(self):
        self.__filters = []

    def addFilter(self, filter):
        self.__filters.append(filter)

    def remove(self, filter):
        self.__filters.remove(filter)

    def doFilter(self, elements):
        for filter in self.__filters:
            elements = filter.doFilter(elements)
        return elements

import re

class SensitiveFilter(Filter):
    """敏感词过滤"""
    def __init__(self):
        self.__sensitives = ["黄色", "反动", "贪污"]

    def doFilter(self elements):
        # 敏感词列表转换成正则表达式
        regex = ""
        for word in self.__sensitives:
            regex += word + "|"
        regex = regex[0:len(regex)-1]

        # 对每个元素进行过滤
        newElements = []
        for element in elements:
            item, num = re.subn(regex, "", element)
            newElements.append(item)
        return newElements


class HtmlFilter(FIlter):
    """HTML特殊字符转换"""
    def __init__(self):
        self.__wordMap = {
            "&": "&amp;",
            "'": " &apos",
            ">": "&gt",
            "<": "&lt",
            "\"": "&quot;",
        }

    def doFIlter(self, elements):
        newElements = []
        for element in elements:
            for key, value in self.__wordMap.items():
                element = element.replace(key, value)
            newElements.append(element)
        return newElements
